#include "Almacenamiento.h"
#include <algorithm>
#include <fstream>

#include "Excepciones.h"

namespace Toolkit
{

Almacenamiento::Almacenamiento(const unsigned long int &tamanoUnidad,
        const TipoSerializacion &tserializacion)
{
    this-> tamanoUnidad = tamanoUnidad;
    this->tamanoUnidadPrimera = tamanoUnidad;
    this-> tserializacion = tserializacion;
    elementos = 0;
}

Almacenamiento::Almacenamiento(const TipoSerializacion &tserializacion)
{
    this-> tserializacion = tserializacion;
}

Almacenamiento::~Almacenamiento()
{
}

unsigned long int Almacenamiento::guardarDato(const stringstream &serializacion)
{
    if (espacios.empty())
    {
        elementos++;
        guardarDato(serializacion, elementos - 1);

        return elementos - 1;

    }
    else
    {
        unsigned long int pos = espacios.front();
        espacios.pop_front();
        guardarDato(serializacion, pos);
        return pos;

    }
}

void Almacenamiento::guardarDato(const stringstream &serializacion,
        const unsigned long int posicion)
{
    string datos = serializacion.str();
    if (datos.size() > getTamano(posicion))
    {
    	throw SerializacionInvalidaException();
    }
    else if (posicion >= elementos)
    {
        throw PosicionInvalidaException();
    }
    else
    {
        int libres = getTamano(posicion) - datos.size();

        if (libres > 0)
        {
            datos.append(libres, '#');
        }
    }

    if (posicion == 0) {
        stream->seekp(offsetElementos, ios_base::beg);
    } else {
        stream->seekp((posicion-1) * getTamano(posicion) + tamanoUnidadPrimera + offsetElementos, ios_base::beg);
    }

    stream->write(datos.c_str(), getTamano(posicion));

    //si la posicion estaba como eliminada, se la saca de la lista
    espacios.remove(posicion);
}

void Almacenamiento::recuperarDato(const unsigned long int posicion,
        stringstream &resultado) const
        {
    if (posicion >= elementos)
    {
        throw PosicionInvalidaException();
    }
    else if ((find(espacios.begin(), espacios.end(), posicion) != espacios.end()))
    {
        throw UnidadEliminadaException();
    }

    char* informacion = new char[getTamano(posicion)];

    if (posicion == 0) {
        stream->seekg( offsetElementos, ios_base::beg);
    } else {
        stream->seekg((posicion-1) * getTamano(posicion) + tamanoUnidadPrimera + offsetElementos, ios_base::beg);
    }

    stream->read(informacion, getTamano(posicion));

    resultado.str("");

    resultado.write(informacion, getTamano(posicion));

    delete[] informacion;
}

void Almacenamiento::eliminarDato(unsigned const long int posicion,
        stringstream &resultado)
{
    recuperarDato(posicion, resultado);
    espacios.push_back(posicion);
}

unsigned long int Almacenamiento::getTamano() const
{
    return tamanoUnidad;
}

unsigned long int Almacenamiento::getTamano(const unsigned long int posicion) const
{
    if (posicion == 0) {
        return tamanoUnidadPrimera;
    } else {
        return tamanoUnidad;
    }
}

unsigned long int Almacenamiento::cantElementos() const
{
    return elementos;
}

list<IndiceEspacio> Almacenamiento::getFragmentacion()
{
    return fragmentacion;
}

void Almacenamiento::setFragmentacion(list<IndiceEspacio> &fragmentacion)
{
    this->fragmentacion = fragmentacion;
}

unsigned long int Almacenamiento::espacioLibre() const
{
    return this->espacios.size();
}

}
